home *** CD-ROM | disk | FTP | other *** search
/ PC World Interactive 7 / PC World Interactive 7.iso / program / asprog.EXE / S.ASM < prev    next >
Assembly Source File  |  1996-07-05  |  30KB  |  1,152 lines

  1. ;TASM source for S.COM v.1.1.  Copyright (C) DA Nye, 1991, all rights reserved.
  2.  
  3. ideal
  4. model tiny
  5.  
  6. NFILES          EQU 200         ;Max # files in display
  7.  
  8. FILESPEC        EQU 82h         ;Address of filespec in command tail
  9. PATH            EQU 81h         ;Count of chars in path part of filespec here
  10. NORMAL_FILE     EQU 11100001b   ;Attribute for a normal file
  11. TAGGED          EQU 80h         ;Bit to set in attrib to indicate file tagged
  12. DTA_ATTRIB      EQU DTA+21      ;Offsets of elements in DTA
  13. DTA_TIME        EQU DTA+22
  14. DTA_DATE        EQU DTA+24
  15. DTA_SIZE        EQU DTA+26
  16. DTA_NAME        EQU DTA+30
  17. FILE_TIME       EQU bp          ;Addrs of elements in 'fileRecords' after name
  18. FILE_DATE       EQU bp+2
  19. FILE_SIZE       EQU bp+4
  20. INVERSE_VIDEO   EQU 70h         ;Black on white
  21. NORMAL_VIDEO    EQU 17h         ;White on blue
  22. COPYVAL         EQU 1           ;Keys for Copy/Move/Delete/Rename operations
  23. DELETEVAL       EQU 2
  24. MOVEVAL         EQU 3
  25. RENAMEVAL       EQU 4
  26.  
  27. codeseg
  28. BOF:
  29. org 100h
  30.  
  31. Start:
  32.     mov sp, OFFSET stackEnd     ;Initialize stack
  33.     sub ax, ax
  34.     push ax
  35.     mov [programSeg], cs        ;Save current value of CS -> program segment
  36.     mov ah, 0Fh                 ;Find display memory
  37.     int 10h
  38.     mov di, 0B000h              ;If mode 7 (= MDA or Herc), video seg=B000h,
  39.     cmp al, 7
  40.     je @@L1
  41.     mov di, 0B800h              ;Otherwise video seg=B800h,
  42. @@L1:
  43.     mov es, di                  ;Unless running under DesqView
  44.     mov cx, 'DE'
  45.     mov dx, 'SQ'
  46.     mov ax, 2B01h
  47.     int 21h
  48.     cmp al, 0FFh
  49.     je @@L2
  50.     mov ah, 0FEh
  51.     int 10h
  52.     mov di, es
  53. @@L2:
  54.     mov [displaySegment], di
  55.     mov es, [2Ch]               ;Find COMSPEC
  56.     sub di, di
  57. @@L3:
  58.     mov si, OFFSET comspec
  59.     mov cx, 8
  60.     repe cmpsb
  61.     jne @@L3
  62.     mov [comspecOff], di
  63.     mov [comspecSeg], es
  64.     mov es, [programSeg]
  65. NewSpec:
  66.     mov si, FILESPEC            ;Examine command line filespec
  67.     mov di, si
  68.     cmp [byte FILESPEC-2], 1    ;If none, use "*.*"
  69.     jbe @@L3
  70. @@L1:
  71.     lodsb
  72.     cmp al, '*'                 ;Look for wildcard chars
  73.     je GetPathLength
  74.     cmp al, '?'
  75.     je GetPathLength
  76.     cmp al, 0Dh
  77.     jne @@L1
  78.     cmp [byte si-2], '\'        ;If none, assume spec is a directory
  79.     jne @@L2
  80.     dec si                      ;If last char was not '\', add it
  81.     jmp @@L3
  82. @@L2:
  83.     mov [byte si-1], '\'
  84. @@L3:
  85.     mov ax, si                  ;Compute length of path minus filename
  86.     sub ax, di
  87.     mov [PATH], al
  88.     mov di, si
  89. NewDir:
  90.     mov [keepSorted], 0         ;Directory is initially unsorted
  91.     mov si, OFFSET starDotStar  ;Append '*.*',0
  92.     call CopyString
  93.     jmp SHORT Restart
  94. NoRoomError:
  95.     mov dx, OFFSET noRoomMsg    ;Not enough room to run S
  96. Abort:
  97.     call BlankScreen
  98.     mov ah, 9
  99.     int 21h
  100.     mov ax, 4C00h               ;Bye
  101.     int 21h
  102.  
  103. GetPathLength:
  104.     mov al, [byte FILESPEC-2]   ;Null-terminate other filespecs
  105.     sub ah, ah
  106.     mov si, ax
  107.     add si, FILESPEC-1
  108.     mov [si], ah
  109.     mov cx, ax                  ;Count chars in path excluding file name:
  110.     std                         ;Search backward for last '\' or ':'
  111. @@L1:
  112.     lodsb
  113.     cmp al, '\'
  114.     je @@L2
  115.     cmp al, ':'
  116.     loopne @@L1
  117. @@L2:
  118.     cld
  119.     mov [PATH], cl
  120.  
  121. Restart:
  122.     call HideCursor
  123.     mov ax, OFFSET files        ;Cursor -> first file
  124.     mov [cursor], ax
  125.     mov [top], ax
  126. Restart0:
  127.     mov bx, (EOF-BOF)/16+1      ;Resize memory to amount needed
  128.     mov ah, 4Ah
  129.     int 21h
  130.     jc NoRoomError
  131.     mov ah, 19h                 ;Get default drive for path/spec display
  132.     int 21h
  133.     add al, 'A'
  134.     mov di, OFFSET pathNSpec    ;Store letter, ':\'
  135.     stosb
  136.     mov ax, '\:'
  137.     stosw
  138.     mov si, di                  ;Get path and name of current directory
  139.     sub dl, dl
  140.     mov ah, 47h
  141.     int 21h
  142.     sub al, al                  ;Find end
  143.     mov cx, -1
  144.     repne scasb
  145.     dec di
  146.     mov ax, ' '+100h*' '        ;Add two spaces
  147.     stosw
  148.     mov si, FILESPEC            ;Append filespec
  149.     call CopyString
  150.  
  151. ;Get all file names matching filespec and set up tables
  152. GetFileRecords:
  153.     mov dx, OFFSET DTA          ;Set up DTA
  154.     mov ah, 1Ah
  155.     int 21h
  156.     mov dx, FILESPEC            ;Get first file name
  157.     mov cl, 37h
  158.     mov ah, 4Eh
  159.     int 21h
  160.     jnc FileFound               ;No files.  Try a different filespec.
  161.     mov si, OFFSET NoFilesMsg
  162.     call Error
  163.     jmp NewFilespec
  164. FileFound:
  165.     mov di, OFFSET fileRecords  ;DI -> storage for file names
  166.     mov bx, OFFSET files        ;BX -> array of files
  167.     sub bx, 2
  168. StoreFileName:
  169.     add bx, 2                   ;For all files that will fit,
  170.     cmp bx, (OFFSET files) + NFILES*2
  171.     jb @@L1
  172.     sub bx, 2
  173.     mov [last], bx
  174.     mov si, OFFSET tooManyMsg
  175.     jmp DoError
  176. @@L1:
  177.     mov [bx], di                ;Store pointer to status/filename in files[]
  178.     mov al, [DTA_ATTRIB]        ;Store status byte
  179.     and al, 3Fh                 ;Top bit is used to indicate file is marked
  180.     stosb
  181.     mov si, OFFSET DTA_NAME     ;Copy file name from DTA to filename storage
  182.     call CopyString
  183.     inc di
  184.     mov si, OFFSET DTA_TIME     ;Copy time, date and size
  185.     mov cx, 4
  186.     rep movsw
  187.     mov ah, 4Fh                 ;Next filename
  188.     int 21h
  189.     jnc StoreFileName
  190.     mov [last], bx              ;Save pointer to last file entry
  191.     mov al, [keepSorted]        ;If returning from EXEC, need to resort files?
  192.     or al, al
  193.     jz DisplayFiles
  194.     jmp Sort0
  195.  
  196. ;Main loop.  Display files and wait for command.
  197. DisplayFiles:
  198.     call BlankStatus            ;Clear status line
  199.     mov si, OFFSET helpF1       ;Display help key
  200.     call DisplayString
  201.     mov bx, [cursor]
  202.     mov si, [bx]
  203.     lodsb                       ;Get attributes of file at cursor
  204.     mov [attrib], al            ;Save attribute byte
  205.     call DisplayString          ;Display name of highlighted file
  206.     mov bp, si                  ;Save pointer to time, date, size
  207.     test [attrib], 10h          ;If a directory,
  208.     jz @@L7
  209.     mov di, 44                  ; show '<DIR>' instead of file size
  210.     mov si, OFFSET dirMsg
  211.     call DisplayString
  212.     jmp SHORT @@L9
  213. @@L7:
  214.     std                         ;File size, right justified
  215.     mov di, 56
  216.     mov ax, [FILE_SIZE]
  217.     mov dx, [FILE_SIZE + 2]
  218.     mov cx, 10000               ;Divide by 10000, show quotient|remainder
  219.     div cx
  220.     xchg ax, dx
  221.     or dx, dx
  222.     jz @@L8
  223.     mov cl, 4
  224.     call WriteDecimal
  225.     mov ax, dx
  226. @@L8:
  227.     sub cl, cl
  228.     call WriteDecimal
  229. @@L9:
  230.     std
  231.     mov di, 74                  ;File date:
  232.     sub dx, dx
  233.     mov bx, [FILE_DATE]         ;Year
  234.     mov al, bh
  235.     shr al, 1
  236.     add al, 80
  237.     sub ah, ah
  238.     mov cl, 2
  239.     call WriteDecimal
  240.     mov al, '/'
  241.     stosw
  242.     mov ax, bx                  ;Day
  243.     and ax, 1Fh
  244.     mov cx, 2
  245.     call WriteDecimal
  246.     mov al, '/'
  247.     stosw
  248.     mov ax, bx                  ;Month
  249.     mov cl, 5
  250.     shr ax, cl
  251.     and ax, 0Fh
  252.     mov cl, 2
  253.     call WriteDecimal
  254.     mov di, 86
  255.     mov bx, [FILE_TIME]         ;File time:
  256.     mov ax, bx                  ;Minutes
  257.     mov cl, 5
  258.     shr ax, cl
  259.     and ax, 3Fh
  260.     mov cx, 2
  261.     call WriteDecimal
  262.     mov al, ':'
  263.     stosw
  264.     mov al, bh                  ;Hours
  265.     mov cl, 3
  266.     shr al, cl
  267.     sub ah, ah
  268.     sub cl, cl
  269.     call WriteDecimal
  270.     cld
  271.     mov di, 90
  272.     mov dl, [attrib]            ;Display attribute letters
  273.     test dl, 1                  ;Read-only
  274.     jz @@L3
  275.     mov al, 'R'
  276.     stosw
  277. @@L3:
  278.     test dl, 2                  ;Hidden
  279.     jz @@L4
  280.     mov al, 'H'
  281.     stosw
  282. @@L4:
  283.     test dl, 4                  ;System
  284.     jz @@L5
  285.     mov al, 'S'
  286.     stosw
  287. @@L5:
  288.     test dl, 20h                ;Archive
  289.     jz @@L6
  290.     mov al, 'A'
  291.     stosw
  292. @@L6:
  293.     mov di, 100                 ;Display path and filespec
  294.     mov si, OFFSET pathNSpec
  295.     call DisplayString
  296.     mov bx, [top]
  297.     mov di, 160
  298. DisplayNext:
  299.     mov ah, NORMAL_VIDEO        ;Set to inverse video if cursor line
  300.     cmp bx, [cursor]
  301.     jne @@L0
  302.     mov ah, INVERSE_VIDEO
  303. @@L0:
  304.     cmp bx, [last]              ;If done with files,
  305.     jle @@L0a
  306.     mov cx, 16                  ;Blank out name area
  307.     jmp @@L7
  308. @@L0a:
  309.     mov si, [bx]                ;Get table entry for a file
  310.     lodsb                       ;Get status byte
  311.     test al, TAGGED             ;If file has been tagged, display '>'
  312.     mov al, '>'
  313.     jnz @@L1
  314.     mov al, ' '
  315. @@L1:
  316.     stosw
  317.     mov cx, 9                   ;In field of 9,
  318. @@L2:
  319.     lodsb                       ;Display filename up to extension
  320.     cmp al, '.'
  321.     je @@L3
  322.     or al, al
  323.     jz @@L6
  324. @@L2a:
  325.     stosw
  326.     loop @@L2
  327. @@L3:
  328.     cmp cx, 9                   ;Check for special cases of '.', '..'
  329.     je @@L2a
  330.     cmp [byte si-2], '.'
  331.     je @@L2a
  332.     mov al, ' '                 ;Else pad with spaces out to 9 chars
  333.     rep stosw
  334. @@L4:
  335.     mov cx, 6                   ;Display extension in field of 6
  336. @@L5:
  337.     lodsb
  338.     or al, al
  339.     jz @@L7
  340.     stosw
  341.     loop @@L5
  342.     jmp SHORT @@L7
  343. @@L6:
  344.     add cx, 6                   ;Just pad with blanks if no extension
  345. @@L7:
  346.     mov al, ' '
  347.     rep stosw
  348.     cmp di, 4000                ;Stop at screenful
  349.     je GetCommand
  350.     cmp di, 3872
  351.     jb @@L8
  352.     sub di, 3808                ;Next column
  353. @@L8:
  354.     add di, 128                 ;Next row
  355.     add bx, 2
  356.     jmp DisplayNext
  357.  
  358. ;Get command
  359. GetCommand:
  360.     mov es, [programSeg]
  361.     mov ah, 8                   ;Get keypress
  362.     sub ch, ch
  363. @@L1:
  364.     inc ch
  365.     int 21h
  366.     or al, al
  367.     jz @@L1
  368.     mov ah, ch                  ;AH = 2 if aux code, 1 if plain ASCII
  369.     call ToUpper
  370.     mov di, OFFSET CommandKeys  ;Look it up, get pointer to routine
  371.     mov cx, NCOMMANDS
  372.     repne scasw
  373.     jne InvalidCommand
  374.     add di, CommandAddrs-CommandKeys-2
  375.     mov bx, [cursor]            ;SI -> file record for highlighted file
  376.     mov si, [bx]
  377.     jmp [word di]               ;Jump to routine
  378. InvalidCommand:
  379.     call Beep
  380.     jmp GetCommand
  381.  
  382. ;********************************* Commands ***********************************
  383.  
  384. Up:
  385.     sub bx, 2
  386.     jmp SHORT NewLine
  387.  
  388. Down:
  389.     add bx, 2
  390.     jmp SHORT NewLine
  391.  
  392. Left:
  393.     sub bx, 48
  394.     jmp SHORT NewLine
  395.  
  396. Right:
  397.     add bx, 48
  398.     jmp SHORT NewLine
  399.  
  400. PageUp:
  401.     sub bx, 238
  402.     jmp SHORT NewLine
  403.  
  404. PageDown:
  405.     add bx, 238
  406.  
  407. NewLine:
  408.     cmp bx, [last]              ;Make sure cursor is still within bounds
  409.     jbe @@L1
  410.     mov bx, [last]
  411. @@L1:
  412.     cmp bx, OFFSET files
  413.     jae @@L2
  414.     mov bx, OFFSET files
  415. @@L2:
  416.     cmp bx, [top]               ;Slide window if off screen
  417.     jae @@L3
  418.     mov [top], bx
  419.     jmp SHORT @@L4
  420. @@L3:
  421.     mov ax, bx
  422.     sub ax, 238
  423.     cmp ax, [top]
  424.     jb @@L4
  425.     mov [top], ax
  426. @@L4:
  427.     mov [cursor], bx
  428.     jmp DisplayFiles
  429.  
  430. Tag:
  431.     test [byte si], NOT NORMAL_FILE  ;Only allow marking of normal files
  432.     jnz @@L1
  433.     xor [byte si], TAGGED
  434. @@L1:
  435.     jmp DisplayFiles
  436.  
  437. Go:
  438.     mov [byte inputString], 0   ;Initialize to no user-entered command tail
  439. Go0:
  440.     lodsb                       ;Get attribute byte
  441.     mov dx, si                  ;Join path and file name
  442.     mov si, PATH
  443.     mov di, OFFSET buffer
  444.     call Join
  445.     test al, 10h                ;If a directory,
  446.     jz DoExec
  447. ChangeDir:
  448.     mov dx, OFFSET buffer
  449.     mov ah, 3Bh                 ;Change to it
  450.     int 21h
  451.     mov [byte PATH], 0          ;No path now
  452.     mov di, FILESPEC            ;Read in contents of new directory
  453.     jmp NewDir
  454. DoExec:
  455.     mov si, dx                  ;Find extension
  456. @@L1:
  457.     lodsb
  458.     cmp al, '.'
  459.     je @@L2
  460.     or al, al
  461.     jne @@L1
  462.     jmp SHORT @@L4
  463. @@L2:
  464.     mov di, OFFSET extensions   ;If .EXE, .COM or .BAT, execute it
  465.     mov dx, si
  466. @@L3:
  467.     mov si, dx
  468.     mov cx, 3
  469.     repe cmpsb
  470.     je @@L5
  471.     sub al, al
  472.     repne scasb
  473.     cmp [byte di], 0
  474.     jne @@L3
  475. @@L4:
  476.     mov si, OFFSET notExecMsg   ;Else error, not an executable file
  477.     jmp DoError
  478. @@L5:
  479.     call BlankScreen
  480.     mov dx, OFFSET buffer       ;If .BAT, need COMMAND.COM, otherwise don't
  481.     cmp [byte di], 0
  482.     jne @@L6
  483.     mov si, OFFSET CC
  484. label DoEdit near               ;Edit function enters here
  485.     mov di, OFFSET EXECCmdLine+1
  486.     call CopyString             ;Store '/c ' or '/c <editor> ' to command tail
  487.     mov dl, cl
  488.     mov si, OFFSET buffer       ;Append path\file
  489.     call CopyString
  490.     add dl, cl
  491.     mov [EXECCmdLine], dl       ;Store length, append CR
  492.     mov [byte di], 13
  493.     mov dx, [comspecOff]
  494.     mov ds, [comspecSeg]
  495. @@L6:
  496.     cmp [byte cs:inputString], 0    ;If a command tail was entered,
  497.     jz DoDOS
  498.     push ds
  499.     mov ds, [cs:programSeg]
  500.     mov di, OFFSET EXECCmdLine+1
  501.     mov al, ' '                 ;Add a space
  502.     stosb
  503.     mov si, OFFSET inputString  ;Add command tail
  504.     call CopyString
  505.     inc cl
  506.     add [EXECCmdLine], cl
  507.     mov [byte di], 13
  508.     pop ds
  509. DoDOS:
  510.     mov cx, bx
  511.     mov bx, (inputString-BOF)/16+1  ;Release unneeded memory
  512.     mov ah, 4Ah
  513.     int 21h
  514.     jc SysErr
  515.     call BlankScreen
  516.     push cx
  517.     mov [cs:temp], sp           ;Do EXEC
  518.     mov bx, OFFSET EXECParams
  519.     mov ax, 4B00h
  520.     int 21h
  521.     mov bx, cs                  ;Restore critical registers
  522.     mov ds, bx
  523.     mov es, bx
  524.     mov ss, bx
  525.     mov sp, [temp]
  526.     pop bx
  527.     jnc @@L2
  528.     mov si, OFFSET sysErrMsg    ;EXEC error
  529.     cmp al, 8                   ;If return code = 8, no room
  530.     jne @@L1
  531.     mov si, OFFSET noExecRoomMsg
  532. @@L1:
  533.     jmp DoError
  534. @@L2:
  535.     mov [byte EXECCmdLine], 0   ;Tidy up
  536.     call HideCursor
  537.     jmp Restart0
  538.  
  539. SysErr:
  540.     mov si, OFFSET sysErrMsg
  541. DoError:
  542.     call Error
  543.     jmp DisplayFiles
  544.  
  545. GoCL:
  546.     mov si, OFFSET commandTailMsg   ;Prompt for command tail
  547.     call Query
  548.     mov si, [bx]
  549.     jmp Go0
  550.  
  551. DOS:
  552.     call BlankScreen
  553.     mov dx, [comspecOff]
  554.     mov ds, [comspecSeg]
  555.     jmp DoDOS
  556.  
  557. Edit:
  558.     lea dx, [si+1]              ;Join path and file name
  559.     mov si, PATH
  560.     mov di, OFFSET buffer
  561.     call Join
  562.     mov si, OFFSET editor       ;Invoke editor
  563.     mov [byte inputString],0
  564.     jmp DoEdit
  565.  
  566. Copy:
  567.     mov [byte CMDR], COPYVAL    ;Set Copy/Move/Delete/Remove key to Copy
  568.     jmp SHORT DoCopy
  569.  
  570. Delete:
  571.     mov [byte CMDR], DELETEVAL  ;Set Copy/Move/Delete/Remove key to Delete
  572.     jmp SHORT DoCMDR
  573.  
  574. Move:
  575.     mov [byte CMDR], MOVEVAL    ;Set Copy/Move/Delete/Rename key to Move
  576.  
  577. DoCopy:
  578.     mov si, OFFSET DestMsg      ;If Copy or Move, prompt for destination
  579.     call Query
  580.     mov di, si
  581.     mov [byte si], '\'          ;Append '\'
  582.     inc si
  583.     mov [temp], si
  584.     cmp [byte CMDR], MOVEVAL
  585.     jne DoCMDR
  586.     mov ax, [word FILESPEC]     ;If Move to same drive,
  587.     mov dx, [word inputString]
  588.     cmp ah, ':'
  589.     je @@L1
  590.     cmp dh, ':'
  591.     jne @@L2
  592. @@L1:
  593.     cmp ax, dx
  594.     jne DoCMDR
  595. @@L2:
  596.     mov [byte CMDR], RENAMEVAL  ; do Rename instead (much faster)
  597. DoCMDR:
  598.     mov bp, OFFSET files - 2    ;For each file
  599. CMDRNext:
  600.     add bp, 2
  601.     cmp bp, [last]
  602.     jbe @@L0
  603.     jmp GetFileRecords
  604. @@L0:
  605.     mov si, [bp]                ;Skip if not tagged
  606.     lodsb
  607.     test al, TAGGED
  608.     jz CMDRNext
  609.     xor [byte si-1], TAGGED     ;Else untag
  610.     mov dx, si
  611.     mov si, PATH                ;Source path\filename -> sourceFileSpec
  612.     mov di, OFFSET sourceFileSpec
  613.     call Join
  614.     cmp [byte CMDR], DELETEVAL  ;If not Deleting
  615.     je @@L4
  616.     mov si, dx                  ;Append current file's name to destination path
  617.     mov di, [temp]
  618.     call CopyString
  619.     cmp [byte CMDR], RENAMEVAL  ;If Rename, do it
  620.     jne @@L2
  621. label DoRename near
  622.     mov dx, OFFSET sourceFileSpec
  623.     mov di, OFFSET inputString
  624.     mov ah, 56h
  625.     int 21h
  626.     jc CantOpen
  627. @@L1:
  628.     jmp CMDRNext
  629. @@L2:
  630.     mov dx, OFFSET sourceFileSpec   ;Copy or Move: open source, dest files
  631.     mov ax, 3D00h
  632.     int 21h
  633.     jc CantOpen
  634.     mov [sourceHandle], ax
  635.     sub cx, cx
  636.     mov dx, OFFSET inputString
  637.     mov ax, 3C00h
  638.     int 21h
  639.     jc CantRename
  640.     mov [destHandle], ax
  641. @@L3:
  642.     mov bx, [sourceHandle]      ;Read a bufferful
  643.     mov cx, 1024
  644.     mov dx, OFFSET buffer
  645.     mov ah, 3Fh
  646.     int 21h
  647.     jc ReadError
  648.     mov bx, [destHandle]        ;Write it
  649.     mov cx, ax
  650.     mov ah, 40h
  651.     int 21h
  652.     jc WriteError
  653.     cmp cx, 1024                ;Loop until done
  654.     je @@L3
  655.     mov ah, 3Eh                 ;Close files
  656.     mov bx, [sourceHandle]
  657.     int 21h
  658.     mov bx, [destHandle]
  659.     int 21h
  660.     cmp [CMDR], MOVEVAL         ;If Move, now do Delete
  661.     jne @@L1
  662. @@L4:
  663.     mov bx, OFFSET sourceFileSpec   ;Delete file
  664.     mov ah, 41h
  665.     int 21h
  666.     jnc @@L1
  667.  
  668. CantOpen:
  669.     mov si, OFFSET cantOpenMsg
  670.     jmp DoError
  671. CantRename:
  672.     mov si, OFFSET cantRenameMsg
  673.     jmp DoError
  674. ReadError:
  675.     mov si, OFFSET readMsg
  676.     jmp DoError
  677. WriteError:
  678.     mov si, OFFSET writeMsg
  679.     jmp DoError
  680.  
  681. NewFilespec:
  682.     mov si, OFFSET newSpecMsg   ;Prompt for new filespec
  683.     call Query
  684.     inc al
  685.     mov [FILESPEC-2], al        ;Store count of chars
  686.     mov si, OFFSET inputString
  687.     mov di, FILESPEC
  688.     call CopyString             ;Copy new filespec to command tail area
  689.     mov [byte di], 0Dh          ;Append CR
  690.     jmp NewSpec                 ;Process new filespec
  691.  
  692. Rename:
  693.     lea dx, [si + 1]            ;Join path and current name
  694.     mov si, PATH
  695.     mov di, OFFSET sourceFileSpec
  696.     call Join
  697.     mov si, OFFSET newNameMsg   ;Prompt for new name of file or directory
  698.     call Query
  699.     jmp DoRename
  700.  
  701. Drive:
  702.     mov si, OFFSET newDriveMsg  ;Prompt for letter of drive to change to
  703.     call QueryChar
  704.     sub al, 'A'
  705.     mov dl, al
  706.     mov ah, 0Eh
  707.     int 21h
  708.     cmp dl, al                  ;Try again if that drive doesn't exist
  709.     jb @@L1
  710.     mov si, OFFSET badDriveMsg
  711.     jmp DoError
  712. @@L1:
  713.     jmp Restart
  714.  
  715. Sort:
  716. ;
  717. ;Sort algorithm:
  718. ; 1) make up array of records {pointer to field to sort | tag}, one for each
  719. ;    file, in 'buffer'.
  720. ; 2) bubble-sort these records
  721. ; 3) copy file record pointers in 'files' to 'buffer' in order of sorted tags
  722. ; 4) copy file record pointers back to 'files' in new order
  723. ;
  724.     mov si, OFFSET sortMsg      ;Prompt for sort field
  725.     call QueryChar
  726.     cmp al, 'N'                 ;Check for legal sort field option
  727.     je Sort0
  728.     cmp al, 'E'
  729.     je Sort0
  730.     cmp al, 'D'
  731.     je Sort0
  732.     mov si, OFFSET genErrorMsg
  733.     jmp DoError
  734. Sort0:
  735.     mov [keepSorted], al        ;Remember for later resorting after EXEC
  736.     mov dl, al
  737.     sub dh, dh                  ;DH = tag (position of file in current order)
  738.     mov di, OFFSET buffer
  739.     mov bx, OFFSET files
  740. @@L1:
  741.     mov si, [bx]                ;Find field to sort:  get pointer to record
  742. @@L1a:
  743.     inc si
  744.     mov cx, si
  745.     cmp dl, 'N'                 ;If Name, already pointing at it
  746.     je @@L4
  747.     sub ah, ah                  ;If Date, find null at end of name
  748.     cmp dl, 'E'                 ;If Ext find '.' or end of name
  749.     jne @@L2
  750.     cmp [byte si], '.'          ;'.' and '..' are special cases
  751.     je @@L1a
  752.     mov ah, '.'
  753. @@L2:
  754.     lodsb
  755.     or al, al
  756.     je @@L3
  757.     cmp al, ah
  758.     jne @@L2
  759. @@L3:
  760.     dec si                      ;If Ext, back up to '.' or null
  761.     cmp dl, 'E'
  762.     je @@L4
  763.     add si, 3                   ;If Date, advance to date field
  764. @@L4:
  765.     mov ax, si                  ;Store pointer to field to sort
  766.     stosw
  767.     mov al, dh                  ;Store tag
  768.     stosb
  769.     inc dh                      ;Bump tag
  770.     add bx, 2                   ;Loop until no more files
  771.     cmp bx, [last]
  772.     jbe @@L1
  773. DoSort:
  774.     lea bp, [di-3]              ;BP -> last
  775.     push bp
  776. @@L0:
  777.     mov bx, OFFSET buffer       ;Do bubble sort
  778. @@L1:
  779.     mov si, [bx]
  780.     cmp [word si-1], '.'        ;Leave '.' and '..' alone
  781.     je @@L4
  782.     mov di, [bx+3]
  783.     cmp dl, 'D'                 ;If sorting Dates, compare one word
  784.     jne @@L2
  785.     cmpsw
  786.     jmp SHORT @@L3
  787. @@L2:
  788.     mov cx, -1                  ;Else compare bytes until not equal
  789.     repe cmpsb
  790. @@L3:
  791.     jbe @@L4                    ;If first field > second
  792.     mov ax, [bx]                ;Exchange field pointers and tags
  793.     xchg ax, [bx+3]
  794.     mov [bx], ax
  795.     mov al, [bx+2]
  796.     xchg al, [bx+5]
  797.     mov [bx+2], al
  798. @@L4:
  799.     add bx, 3                   ;Loop until no more files this pass
  800.     cmp bx, bp
  801.     jb @@L1
  802.     sub bp, 3
  803.     cmp bp, OFFSET buffer       ;Loop until no more passes
  804.     jne @@L0
  805. OrderByTags:
  806.     mov di, OFFSET buffer       ;Arrange file pointers in order of tags
  807.     mov si, OFFSET files
  808.     pop bp
  809. @@L1:
  810.     mov bl, [di+2]              ;Get tag
  811.     sub bh, bh
  812.     add bx, bx
  813.     mov ax, [bx+si]             ;Get file ptr associated with that tag
  814.     stosw                       ;Store in place of field pointer in sort buffer
  815.     inc di
  816.     cmp di, bp
  817.     jbe @@L1
  818.     mov si, OFFSET buffer
  819.     mov di, OFFSET files
  820. @@L2:
  821.     movsw                       ;Copy file pointers back in new order
  822.     inc si
  823.     cmp di, [last]
  824.     jbe @@L2
  825.     jmp DisplayFiles
  826.  
  827. Help:
  828.     call BlankStatus            ;Display help on status line
  829.     mov si, OFFSET helpMsg
  830.     call DisplayString
  831.     mov ah, 8
  832.     int 21h
  833.     jmp DisplayFiles
  834.  
  835. Exit:
  836.     call BlankScreen
  837.     mov ax, 4C00h               ;Bye
  838.     int 21h
  839.  
  840. ;******************************* Subroutines **********************************
  841.  
  842. WriteDecimal:
  843. ;
  844. ;Display a decimal number in inverse video, writing digits backwards from right.
  845. ;  IN: AX = number, CL = field width with leading 0s (no leading 0s if CL = 0)
  846. ;      ES:DI -> Video RAM where rightmost digit will go
  847. ; OUT: AH = INVERSE_VIDEO
  848. ;USED: AL, CX
  849.     push bx
  850.     push dx
  851.     mov bx, 10
  852.     sub ch, ch
  853. @@L1:
  854.     sub dx, dx                  ;Get a digit
  855.     div bx
  856.     xchg ax, dx                 ;Write it to display, right to left
  857.     add ax, '0' + INVERSE_VIDEO*100h
  858.     stosw
  859.     xchg ax, dx
  860.     cmp cx, 0                   ;If CX > 0, loop even if AX = 0
  861.     jg @@L2
  862.     or ax, ax                   ;Else loop only if AX > 0 (more digits left)
  863.     jz @@L3
  864. @@L2:
  865.     loop @@L1
  866. @@L3:
  867.     pop dx
  868.     pop bx
  869.     mov ah, INVERSE_VIDEO
  870. Ret1:
  871.     ret
  872.  
  873. DisplayString:
  874. ;
  875. ;Display string on status line.
  876. ;  IN: SI -> string (null-terminated), ES:DI -> status line, AH = attribute
  877. ; OUT: DI is advanced past end of string
  878. ;USES: AL SI
  879. @@L1:
  880.     lodsb
  881.     or al, al
  882.     jz Ret1
  883.     stosw
  884.     jmp @@L1
  885.  
  886. Error:
  887. ;
  888. ;Beep, display string on status line and wait for keypress (any key)
  889. ;  IN: SI -> string
  890. ; OUT: ES -> program segment
  891. ;USED: AX
  892.     call Beep
  893.     call BlankStatus
  894.     call DisplayString          ;Display error string
  895.     mov si, OFFSET ErrorMsg     ;Display 'Press any key' message
  896.     call DisplayString
  897.     mov ah, 8
  898.     int 21h
  899.     mov es, [programSeg]
  900.     ret
  901.  
  902. Query:
  903. ;
  904. ;Prompt for string input on status line
  905. ;  IN: SI -> message
  906. ; OUT: SI -> null at end of ASCIIZ string input, AX = length (excluding null)
  907. ;USED: none
  908.     push bx
  909.     push cx
  910.     push dx
  911.     push di
  912.     push es
  913.     call BlankStatus
  914.     call DisplayString
  915.     mov dx, di                  ;Set cursor to end of printed string
  916.     add dx, 2
  917.     shr dl, 1
  918.     sub dh, dh
  919.     mov ah, 2
  920.     sub bx, bx
  921.     int 10h
  922.     mov cx, 80                  ;Get input
  923.     mov dx, OFFSET inputString
  924.     mov si, dx
  925.     mov ah, 3Fh
  926.     int 21h
  927.     sub ax, 2
  928.     add si, ax
  929.     mov [byte si], 0            ;Null-terminate it
  930.     call HideCursor             ;Hide cursor
  931.     pop es
  932.     pop di
  933.     pop dx
  934.     pop cx
  935.     pop bx
  936.     ret
  937.  
  938. QueryChar:
  939. ;
  940. ;Prompt for single character input
  941. ;  IN: SI -> message
  942. ; OUT: AL = character (lower case converted to upper)
  943. ;USED: AH
  944.     push es
  945.     call BlankStatus
  946.     call DisplayString          ;Display string
  947.     mov ah, 8                   ;Get char
  948.     int 21h
  949.     call ToUpper
  950.     pop es
  951.     ret
  952.  
  953. BlankStatus:
  954. ;
  955. ;Clear top line of display to inverse video
  956. ;  IN: none
  957. ; OUT: ES = video segment, DI = 0
  958. ;USED: AX CX
  959.     mov ax, [displaySegment]
  960.     mov es, ax
  961.     sub di, di
  962.     mov ax, ' ' + INVERSE_VIDEO*100h
  963.     mov cx, 80
  964.     rep stosw
  965.     sub di, di
  966.     ret
  967.  
  968. Join:
  969. ;
  970. ;Copy counted path, then ASCIIZ file name to buffer and null-terminate
  971. ;  IN: SI -> path, DX -> file, DI -> destination
  972. ; OUT: DI -> null at end of copied string, CX = total chars excluding null
  973. ;USED: none
  974.     push ax
  975.     push si
  976.     lodsb                       ;Get count of chars in path (a counted string)
  977.     sub ah, ah
  978.     mov cx, ax
  979.     rep movsb                   ;Copy it to destination
  980.     mov cx, ax
  981.     mov si, dx                  ;Now copy file name (null-terminated)
  982.     call CopyString
  983.     add cx, ax                  ;Sum string counts -> CX
  984.     mov [byte di], 0            ;Null-terminate the result
  985.     pop si
  986.     pop ax
  987.     ret
  988.  
  989. CopyString:
  990. ;
  991. ;Copy null-terminated string.
  992. ;  IN: SI -> string, DI -> destination
  993. ; OUT: CX = length (excluding null), DI -> terminating null of copied string
  994. ;USED: AL
  995.     mov cx, -1
  996. @@L1:
  997.     lodsb                       ;Copy string
  998.     stosb
  999.     or al, al                   ;Until null at end is encountered
  1000.     loopnz @@L1                 ;Accumulate count of chars
  1001.     neg cx                      ;Adjust count
  1002.     sub cx, 2
  1003.     dec di                      ;DI -> terminating null
  1004.     ret
  1005.  
  1006. HideCursor:
  1007. ;
  1008. ;Move cursor off bottom of screen
  1009. ;  IN: none
  1010. ; OUT: none
  1011. ;USED: AH BH DX
  1012.     mov dx, 1900h
  1013.     sub bh, bh
  1014.     mov ah, 2
  1015.     int 10h
  1016.     ret
  1017.  
  1018. Beep:
  1019. ;
  1020. ;Output a bell char
  1021. ;  IN: none
  1022. ; OUT: none
  1023. ;USED: AH DL
  1024.     mov dl, 7
  1025.     mov ah, 2
  1026.     int 21h
  1027.     ret
  1028.  
  1029. BlankScreen:
  1030. ;
  1031. ;Clear screen and home cursor
  1032. ;  IN: none
  1033. ; OUT: none
  1034. ;USED: AX
  1035.     push bx
  1036.     push cx
  1037.     push dx
  1038.     push es
  1039.     push di
  1040.     mov es, [cs:displaySegment] ;Blank screen
  1041.     sub di, di
  1042.     mov ax, ' '+NORMAL_VIDEO*100h
  1043.     mov cx, 25 * 80
  1044.     rep stosw
  1045.     sub dx, dx                  ;Put cursor at upper L hand corner
  1046.     sub bh, bh
  1047.     mov ah, 2
  1048.     int 10h
  1049.     pop di
  1050.     pop es
  1051.     pop dx
  1052.     pop cx
  1053.     pop bx
  1054.     ret
  1055.  
  1056. ToUpper:
  1057. ;
  1058. ;Convert lower to upper case
  1059. ;  IN: AL = char
  1060. ; OUT: AL = char
  1061. ;USED: none
  1062.     cmp al, 'a'
  1063.     jb @@L1
  1064.     cmp al, 'z'
  1065.     ja @@L1
  1066.     add al, 'A'-'a'
  1067. @@L1:
  1068.     ret
  1069.  
  1070. ;********************************** Data **************************************
  1071.  
  1072.  
  1073. ;Command dispatch table:  aux,2 or ASCII,1 paired to command routine addresses
  1074.  
  1075. commandKeys     db 72,2, 80,2, 75,2, 77,2, 73,2, 81,2, 'T',1, 13,1, 'C',1
  1076.                 db 'D',1, 'M',1, 'R',1, 'E',1, 'F',1, 'V',1, 'S',1
  1077.                 db 60,2, 10,1, 59,2, 27,1
  1078. commandAddrs    dw Up, Down, Left, Right, PageUp, PageDown, Tag, Go, Copy
  1079.                 dw Delete, Move, Rename, Edit, NewFilespec, Drive, Sort
  1080.                 dw DOS, GoCL, Help, Exit
  1081. NCOMMANDS       EQU (commandAddrs-commandKeys)/2
  1082.  
  1083.  
  1084. ;Strings
  1085.  
  1086. helpMsg         db 'Copy Delete Edit Filespec Move Ren Sort '
  1087.                 db 'Tag driVe Enter=cd/run F2=DOS Esc=exit',0
  1088. helpF1          db 'Help F1',186,0
  1089. noFilesMsg      db 'No matching files or invalid path',0
  1090. noRoomMsg       db 'Out of room$'
  1091. cantRenameMsg   db "Can't rename to that",0
  1092. badPathMsg      db 'Bad path',0
  1093. sysErrMsg       db 'System error',0
  1094. cantOpenMsg     db "Can't open file",0
  1095. writeMsg        db 'Write error',0
  1096. readMsg         db 'Read error',0
  1097. notExecMsg      db 'Not a directory or executable file',0
  1098. badDriveMsg     db "Drive doesn't exist",0
  1099. genErrorMsg     db 'Error',0
  1100. tooManyMsg      db 'Too many files',0
  1101. noExecRoomMsg   db 'Not enough memory',0
  1102. ErrorMsg        db '.  Press any key.',0
  1103. destMsg         db 'Where to?',0
  1104. newSpecMsg      db 'New filespec:',0
  1105. newDriveMsg     db 'New drive:',0
  1106. sortMsg         db 'Sort on:  Name Ext Date',0
  1107. newNameMsg      db 'New name:',0
  1108. commandTailMsg  db 'Command tail:',0
  1109. dirMsg          db '<DIR>',0
  1110. extensions      db 'EXECOMBAT',0
  1111. starDotStar     db '*.*',0
  1112. editor          db '/C E ', 12 dup (0)
  1113. cc              db '/C ',0
  1114. comspec         db  'COMSPEC='
  1115.  
  1116.  
  1117. ;EXEC function parameter block
  1118.  
  1119. EXECParams      dw 0
  1120. EXECCmdLineOff  dw OFFSET EXECCmdLine
  1121. programSeg      dw 0
  1122.                 dw -1, -1 , -1 , -1
  1123.  
  1124. EXECCmdLine     db 0, 80 dup (?)
  1125. sourceFileSpec  EQU EXECCmdLine     ;Second use for this space during Copy etc.
  1126.  
  1127.  
  1128. ;Variables, buffers
  1129.  
  1130.                 dw 128 dup (?)  ;Stack (here for protection during EXEC)
  1131. stackEnd:
  1132. keepSorted      db ?            ;Holds sort subcommand char if this dir sorted
  1133. CMDR            db ?            ;Key for Copy/Move/Delete/Rename actions
  1134. attrib          db ?            ;File attribute byte
  1135. sourceHandle    dw ?            ;Source handle for Copy, etc.
  1136. destHandle      dw ?            ;Destination handle for Copy etc.
  1137. cursor          dw ?            ;Position in 'files' of highlighted file
  1138. last            dw ?            ;Position in 'files' of last file in directory
  1139. top             dw ?            ;Position in 'files' of file at top of screen
  1140. temp            dw ?            ;Holds SP during EXEC, other uses
  1141. comspecSeg      dw ?            ;Segment of environment
  1142. comspecOff      dw ?            ;Offset of 'C:\COMMAND.COM' in environment
  1143. displaySegment  dw ?            ;Segment of display RAM
  1144. pathNSpec       db 80 dup (?)   ;Default path and current filespec for display
  1145. DTA             db 64 dup (?)   ;Disk Transfer Area
  1146. inputString     db 80 dup (?)   ;String returned by 'Query', other uses
  1147. files           dw NFILES dup (?)   ;Array of pointers to file records
  1148. fileRecords     db 80*NFILES dup (?)    ;File records: attrib/name/time/date
  1149. buffer          db 1024 dup (?)     ;Buffer for Copy, etc., other uses
  1150.  
  1151. EOF:
  1152. end Start